package com.spring.factory.impl;

import com.spring.annotation.*;
import com.spring.aware.*;
import com.spring.bean.BeanDefinition;
import com.spring.context.impl.AbstractApplicationContext;
import com.spring.beanpostprocessor.BeanPostProcessor;
import com.spring.util.ClassUtils;
import com.spring.util.StringUtils;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.Resource;
import java.lang.reflect.*;
import java.util.*;

/**
 * 这是对抽象工厂 能力的进一步增加，补充了自动注入的能力 ,定义了创建bean 的逻辑
 *
 * 依然没有获得 bean 定义信息的能力，留给超级工厂处理
 */

@Slf4j
public abstract class AbstractAutowireBeanFactory extends AbstractBeanFactory {
	// 持有容器对象，方便从容器中获取bean 定义信息
	protected AbstractApplicationContext ac;
	
	// 保存所有 BeanPostProcessor  实例对象，方便在普通 bean 后置处理器直接调用处理
	protected ArrayList<BeanPostProcessor> beanPostProcessors = new ArrayList<>();
	
	
	public AbstractAutowireBeanFactory(AbstractApplicationContext ac) {
		this.ac = ac;
	}
	
	/**
	 * 向工厂添加 BeanPostProcessor
	 */
	public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
		beanPostProcessors.add(beanPostProcessor);
	}
	
	/**
	 * 向工厂添加 BeanPostProcessor
	 */
	public void addBeanPostProcessor(List<BeanPostProcessor> beanPostProcessor) {
		beanPostProcessors.addAll(beanPostProcessor);
	}
	
	
	@SuppressWarnings("unchecked")
	public <T> List<T> getBeanList(Class<T> clazz) {
		List<T> list = new ArrayList<>();
		ac.getAllBeanNameByType(clazz).forEach(beanName -> {
			T bean = (T) getBean(beanName);
			list.add(bean);
		});
		return list;
	}
	
	@SuppressWarnings("unchecked")
	public <T> Map<String, T> getBeanMap(Class<T> clazz) {
		Map<String, T> map = new HashMap<>();
		ac.getAllBeanNameByType(clazz).forEach(beanName -> {
			T bean = (T) getBean(beanName);
			map.put(beanName, bean);
		});
		return map;
	}
	
	
	/**
	 * 创建 bean 的核心逻辑
	 */
	@Override
	protected Object createBean(String beanName) {
		BeanDefinition beanDefinition = ac.getBeanDefinition(beanName);
		// 查找一下 beanDefinition,没有定义是无法创建的
		if (beanDefinition == null) {
			throw new RuntimeException("没有找到[" + beanName + "]的BeanDefinition信息");
		}
		return createBean(beanName, beanDefinition);
	}
	
	
	/**
	 * 根据 bean 定义创建 bean
	 */
	private Object createBean(String beanName, BeanDefinition bd) {
		Class<?> resolvedClass = bd.getBeanClass();
		
		// 实例化之前还需要做一些工作
		// 给BeanPostProcessor 一个机会返回一个 bean 来代替掉 目标bean
		// 一旦返回值不为null ，则不会走 doCreateBean 的流程
		
		// 返回null 表示继续走正常的创建流程
		Object bean = resolveBeforeInstantiation(beanName, bd);
		if (bean != null) {
			return bean;
		}
		
		Object beanInstance = doCreateBean(resolvedClass, bd);
		return beanInstance;
	}
	
	
	/**
	 * 返回null 表示继续走正常的创建流程, 返回非null 就表示 这个对象就是 bean 了，不需要再走后续的 BeanPostProcessor 了
	 */
	public Object resolveBeforeInstantiation(String beanName, BeanDefinition mbd) {
		Object bean = null;
		for (BeanPostProcessor bp : beanPostProcessors) {
			
			// 从 BeanPostProcessor 中查找 InstantiationAwareBeanPostProcessor
			// 该接口允许在 实例化之前
			
			if (bp instanceof InstantiationAwareBeanPostProcessor) {
				bean = ((InstantiationAwareBeanPostProcessor) bp).postProcessBeforeInstantiation(mbd.getBeanClass(), beanName);
				if (bean != null) {
					
					// 如果不为null, 则默认这个bean 已经完成了实例化，初始化，属性注入， 直接做一下最后的 后置处理器
					bean = applyBeanPostProcessorAfterInitialization(bean, beanName);
					return bean;
				}
			}
		}
		return null;
	}
	
	
	/**
	 * 实际的创建方法,核心创建逻辑
	 */
	private Object doCreateBean(Class<?> resolvedClass, BeanDefinition bd) {
		String beanName = bd.getBeanName();
		
		
		// 实例化之前
		Object bean = resolveBeforeInstantiation(beanName, bd);
		if (bean != null) {
			// 返回非 null 表示继续不需要走后续流程
			return bean;
		}
		
		// 首先实例化
		// beanInstance 是 实际bean对象
		final Object beanInstance = createBeanInstance(resolvedClass, bd);
		
		
		// 标记为创建中 ，并加入三级缓存
		remarkInCreating(beanName);
		addObjectFactory(beanName, () -> getEarlyBeanReference(beanName, beanInstance));
		
		// 属性填充
		populateBean(beanName, bd, beanInstance);
		
		
		// 开始初始化操作 ，包括 BeanPostProcessor 在内
		// 代理什么的都在这里面， 返回的对象是一个 暴露出去的对象，不一定是原始对象
		Object exposedObject = initializeBean(beanName, bd, beanInstance);
		
		// 结束初始化操作,是单例加入单例池缓存起来
		// 移除二三级缓存，并标记创建完成，如果是单例
		removeObjectFactory(beanName);
		removeEarlyObject(beanName);
		remarkCreateFinished(beanName);
		if (bd.isSingleton()) {
			addSingleton(beanName, exposedObject);
		}
		
		
		log.debug("已创建 {} 对象", resolvedClass.getName());
		
		// 返回结束暴露出来的对象，不要返回原始对象
		return exposedObject;
	}
	
	
	/**
	 * 创建bean 实例  ,可能来自 @component 注解对象，也可能是 @Bean
	 *
	 * @param resolvedClass 从BeanDefinition 中解析获得的beanClass
	 */
	public Object createBeanInstance(Class<?> resolvedClass, BeanDefinition bd) {
		Object obj = null;
		try {
			if (bd.isFactoryBean()) {
				// 是工厂的话，再判断一次
				String factoryBeanName = bd.getFactoryBeanName();
				String factoryMethodName = bd.getFactoryMethodName();
				
				// 拿到工厂对象
				Object factoryBean = getBean(factoryBeanName);
				
				// 当前默认工厂方法全是无参数构造方法
				Method method = factoryBean.getClass().getMethod(factoryMethodName);
				obj = method.invoke(factoryBean);
				
			} else {
				// 不是工厂则直接 无参构造实例化
				obj = resolvedClass.newInstance();
			}
		} catch (InstantiationException | IllegalAccessException | NoSuchMethodException e) {
			log.error("实例化对象{}->{} 时失败", bd.getBeanName(), bd.getBeanClass().getName());
			throw new RuntimeException("实例化对象出错");
		} catch (InvocationTargetException e) {
			log.error("实例化对象{}->{} 时失败,内部方法出错,error:{}", bd.getBeanName(), bd.getBeanClass().getName(), e.getTargetException().getMessage());
			throw new RuntimeException("调用方法时出错");
		}
		return obj;
	}
	
	
	/**
	 * 属性注入
	 */
	protected void populateBean(String beanName, BeanDefinition bd, Object obj) {
		// 在对象实例化之后， 这个时间点， 调用 InstantiationAwareBeanPostProcessor 处理对象
		// 如果返回true 表示继续后续逻辑，返回false，表示对象处理完毕
		for (BeanPostProcessor bp : beanPostProcessors) {
			if (bp instanceof InstantiationAwareBeanPostProcessor) {
				InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
				
				// 是否继续逻辑处理逻辑
				boolean continueFlag = ibp.postProcessAfterInstantiation(obj, beanName);
				if (!continueFlag) {
					return;
				}
			}
		}
		
		// 开始遍历字段进行注入操作
		Class<?> clazz = bd.getBeanClass();
		List<Field> allFields = ClassUtils.findField(clazz);
		for (Field field : allFields) {
			// 字段上没有 autowired 或 resource 则忽略
			if (!field.isAnnotationPresent(Autowired.class) && !field.isAnnotationPresent(Resource.class)) {
				continue;
			}
			
			// 最终要注入的值
			Object val = getInjectVal(field);
			if (val == null) {
				continue;
			}
			// 通用反射赋值方法
			ClassUtils.assignValueToField(obj, field, val);
		}
		// 完成所有字段赋值注入操作
	}
	
	
	/**
	 * 获取要注入字段的值
	 */
	private Object getInjectVal(Field field) {
		Type type = field.getGenericType();
		if (type instanceof Class) {
			// 常规bean
			Qualifier qualifier = field.getAnnotation(Qualifier.class);
			if (qualifier == null) {
				// 按类型注入
				return ac.getBeanByType(((Class<?>) type));
			} else {
				// 按名字注入
				String beanName = StringUtils.isNotEmpty(qualifier.value()) ? qualifier.value() : StringUtils.toLowerFirstCase(field.getName());
				return getBean(beanName);
			}
		} else if (type instanceof ParameterizedType) {
			Type[] actualTypes = ((ParameterizedType) type).getActualTypeArguments();
			// List<T> 类型
			if (type instanceof List && actualTypes.length == 1 && actualTypes[0] instanceof Class) {
				return getBeanList(((Class<?>) actualTypes[0]));
			} else if (type instanceof Map) {
				// Map<String,T> 类型
				if (actualTypes.length == 2 && String.class == actualTypes[0] && actualTypes[1] instanceof Class) {
					return getBeanMap(((Class<?>) actualTypes[1]));
				}
			}
		}
		return null;
	}
	
	
	/**
	 * beanInstance 的初始化
	 * 要再 属性注入后面，因为要调用用户代码了
	 */
	protected Object initializeBean(String beanName, BeanDefinition bd, Object beanInstance) {
		// 处理aware回调
		invokeAwareMethods(beanName, beanInstance);
		
		// 应用 BeanPostProcessor 的 初始化之前的方法
		Object exposeObject = beanInstance;
		exposeObject = applyBeanPostProcessorBeforeInitialization(exposeObject, beanName);
		
		// 初始化已结束
		// InitializingBean 接口调用
		if (beanInstance instanceof InitializingBean) {
			((InitializingBean) beanInstance).afterPropertiesSet();
		}
		
		// 反射调用 初始化方法 ，必须无参函数
		String initMethodName = bd.getInitMethodName();
		if (StringUtils.isNotEmpty(initMethodName)) {
			try {
				Method method = beanInstance.getClass().getMethod(initMethodName);
				method.invoke(beanInstance);
			} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
				log.error("调用对象{}的初始化方法 {} 时出现错误", beanName, initMethodName);
			}
		}
		
		// 最后应用 后置处理器的 初始化之后方法，这里面有代理逻辑
		exposeObject = applyBeanPostProcessorAfterInitialization(beanInstance, beanName);    // 遍历BeanPostProcessor ，逐个处理
		return exposeObject;
	}
	
	/**
	 * aware 回调实现,就实现2个，其他不管了
	 */
	public void invokeAwareMethods(String beanName, Object bean) {
		if (bean instanceof BeanNameAware) {
			((BeanNameAware) bean).setBeanName(beanName);
		}
		if (bean instanceof BeanFactoryAware) {
			((BeanFactoryAware) bean).setBeanFactory(this);
		}
	}
	
	/**
	 * 获取早期的bean引用 用于提前创建aop 之类
	 *
	 *
	 * 容器创建bean的时候,会将 一个 ()->getEarlyBeanReference()  lambda 对象放进三级缓存
	 *
	 * 然后继续执行 属性注入
	 * 初始化：
	 *      处理aware 回调
	 *      应用 beanpostprocessor 的 before
	 *      调用初始化方法
	 *
	 *      最后 应用 BeanPostProcessor 的 After(代理在这里实现)
	 *
	 *
	 * 正常情况下：
	 * 1. bean A  将自身放进三级缓存中，然后走属性注入，回调， before ，初始化， After 方法 ，最终返回 暴露对象回去
	 *
	 * 特殊情况下:
	 * 1. bean A 将自身放进三级缓存中，然后走属性注入
	 *
	 *          发现依赖以 bean  B   ，准备创建 B
	 *
	 *              B  也把自身放进三级缓存，走属性注入，发现依赖A ，寻找A的时候，一级缓存没有，二级缓存没有，三级缓存找到了
	 *              从三级缓存中获取
	 *                  并执行里面的逻辑，也就是 这里的 getEarlyBeanReference  把 A  的  After 流程先走掉 ，得到 A 的暴露对象而非原始对象
	 *
	 *                  同时  A的暴露对象加入到二级缓存中，从三级缓存中删除 A
	 *
	 *
	 *              完成 B 的 后续处理操作
	 *
	 *          正常得到 B 的暴露对象，完成属性注入，继续 回调 ,before ，初始
	 *
	 *     走 After 方法 时，尝试从 二级缓存查找
	 *          如果二级缓存没有，表示自身对象依然在 三级缓存，没有被其他人提前获取自己的暴露对象， 正常应用所有的后置处理器
	 *          如果二级缓存已经有了， 说明其他对象 提前获取了 A 的暴露对象， A 已经走完了 后置处理器，这里不需要再处理一次了
	 *                  那么直接返回 二级缓存中 的提前暴露对象即可
	 *
	 *     完成A的创建
	 */
	public Object getEarlyBeanReference(String beanName, Object rawObj) {
		// 从二级缓存获取，如果二级缓存有就直接返回
		Object earlySingletonObject = getEarlySingletonObject(beanName);
		if (earlySingletonObject != null) {
			return earlySingletonObject;
		}
		// 二级缓存没有，用这个原始对象走完后置处理器，返回最终的暴露对象， 延迟
		return applyBeanPostProcessorAfterInitialization(rawObj, beanName);
	}
	
	
	// 应用所有的后置处理器 before 方法
	protected Object applyBeanPostProcessorBeforeInitialization(Object bean, String beanName) {
		// spring 的逻辑是： beanPostProcessor 如果返回一个 null ，则不再走后续的beanPostProcessor
		// 并且返回 这个 beanPostProcessor() 前的结果
		Object result = bean;
		
		for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
			Object current = beanPostProcessor.postProcessBeforeInitialization(result, beanName);
			if (current == null) {
				return result;
			}
			result = current;
		}
		return result;
	}
	
	// 应用所有的后置处理器 after 方法
	// 需要检查下 对象是否已经经过了后置处理器，如果在三级缓存，则表示没有过；  在二级缓存，说明已经经过了
	protected Object applyBeanPostProcessorAfterInitialization(Object bean, String beanName) {
		Object result = getEarlySingletonObject(beanName);
		if (result != null) {
			return result;      // 能在二级缓存中找到说明提前aop过了
		}
		
		// 逐个应用后置处理器
		result = bean;
		for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
			Object current = beanPostProcessor.postProcessAfterInitialization(result, beanName);
			if (current == null) {
				return result;
			}
			result = current;
		}
		return result;
	}
	
	
	/**
	 * 实例化下剩下的所有的普通单例bean
	 */
	public void instantSingletons() {
		// 后面再实例化剩余的其他普通 单例 bean  原型不实例化
		String[] beanDefinitionNames = ac.getBeanDefinitionNames();
		for (String beanName : beanDefinitionNames) {
			// 跳过已经创建完成的
			if (isFinishCreated(beanName)) {
				continue;
			}
			
			BeanDefinition beanDefinition = ac.getBeanDefinition(beanName);
			if (beanDefinition.isSingleton()) {
				getBean(beanName);
			}
		}
	}
	
	/**
	 * 销毁所有单例bean
	 */
	public void destroySingletons() {
		String[] beanDefinitionNames = ac.getBeanDefinitionNames();
		for (String beanName : beanDefinitionNames) {
			
			// 存在于单例池中的对象才调用 销毁方法，原型对象都是给调用者处理的
			if (isFinishCreated(beanName)) {
				Object singleton = getSingletonObject(beanName);
				
				// 如果是这个接口，直接调用
				if (singleton instanceof DisposableBean) {
					((DisposableBean) singleton).destroy();
					continue;
				}
				
				String destroyMethodName = ac.getBeanDefinition(beanName).getDestroyMethodName();
				if (StringUtils.isNotEmpty(destroyMethodName)) {
					
					// 暂时只实现一个 无参数的销毁方法调用 ，其他先不管
					invokeNoArgsDestroyMethod(beanName, singleton, destroyMethodName);
				}
			}
			
		}
	}
	
	private void invokeNoArgsDestroyMethod(String beanName, Object singleton, String destroyMethodName) {
		try {
			Method method = singleton.getClass().getMethod(destroyMethodName);
			method.invoke(singleton);
			log.error("已销毁对象{}", beanName);
		} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
			log.error("调用对象{}的销毁方法 {} 时出现错误", beanName, destroyMethodName);
		}
	}
}
